<?php

namespace EasyDB\Tools;

class Strings
{
    /**
     * 根据生日计算年龄
     * @param string $birthday 生日时间
     * @return int
     */
    public static function getBirthdayAge(string $birthday)
    {
        if ($birthday == null) {
            return 0;
        }
        list($year, $month, $day) = explode('-', date('Y-m-d'));
        list($bYear, $bMonth, $bDay) = explode('-', date('Y-m-d', strtotime($birthday)));
        $age = $year - $bYear;
        if ($month < $bMonth) {
            --$age;
        }
        if ($month == $bMonth && $day < $bDay) {
            --$age;
        }
        $age <= 0 and $age = 0;
        return $age;
    }


    /**
     * 检测是否是url
     * @param string $url 网址
     * @return bool
     */
    public static function checkUrl(string $url)
    {
        $pattern = "/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/";
        if (!preg_match($pattern, $url)) {
            return false;
        }
        return true;
    }


    /**
     * 检查邮箱是否正确
     * @param string $email 邮箱地址
     * @return bool
     */
    public static function checkEmail(string $email)
    {
        $num = preg_match("/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/", $email, $match);
        if ($num == 0) {
            return false;
        }
        return true;
    }


    /**
     * 检查手机号码是否正确
     * @param int $mobile 手机号
     * @return bool
     */
    public static function checkMobile(int $mobile)
    {
        $num = preg_match("/^13[0-9]{1}[0-9]{8}$|15[0-9]{1}[0-9]{8}$|18[0-9]{1}[0-9]{8}$/", $mobile, $match);
        if ($num == 0) {
            return false;
        }
        return true;
    }


    /**
     * 校验身份证号是否合法
     * @param string $num 待校验的身份证号
     * @return bool
     */
    public static function checkIDCardValid(string $num)
    {
        //老身份证长度15位，新身份证长度18位
        $length = strlen($num);
        if ($length == 15) { //如果是15位身份证

            //15位身份证没有字母
            if (!is_numeric($num)) {
                return false;
            }
            // 省市县（6位）
            $areaNum = substr($num, 0, 6);
            // 出生年月（6位）
            $dateNum = substr($num, 6, 6);
        } elseif ($length == 18) { //如果是18位身份证
            //基本格式校验
            if (!preg_match('/^\d{17}[0-9xX]$/', $num)) {
                return false;
            }
            // 省市县（6位）
            $areaNum = substr($num, 0, 6);
            // 出生年月日（8位）
            $dateNum = substr($num, 6, 8);
        } else { //假身份证
            return false;
        }

        //验证地区
        if (!self::isAreaCodeValid($areaNum)) {
            return false;
        }

        //验证日期
        if (!self::isDateValid($dateNum)) {
            return false;
        }

        //验证最后一位
        if (!self::isVerifyCodeValid($num)) {
            return false;
        }

        return true;
    }

    /**
     * 省市自治区校验
     * @param string $area 省、直辖市代码
     * @return bool
     */
    private static function isAreaCodeValid(string $area)
    {
        $provinceCode = substr($area, 0, 2);
        if (11 <= $provinceCode && $provinceCode <= 65) {
            return true;
        }
        return false;
    }

    /**
     * 验证出生日期合法性
     * @param string $date 日期
     * @return bool
     */
    private static function isDateValid(string $date)
    {
        if (strlen($date) == 6) { //15位身份证号没有年份，这里拼上年份
            $date = '19' . $date;
        }
        $year  = intval(substr($date, 0, 4));
        $month = intval(substr($date, 4, 2));
        $day   = intval(substr($date, 6, 2));

        //日期基本格式校验
        if (!checkdate($month, $day, $year)) {
            return false;
        }

        //日期格式正确，但是逻辑存在问题(如:年份大于当前年)
        $currYear = date('Y');
        if ($year > $currYear) {
            return false;
        }
        return true;
    }

    /**
     * 验证18位身份证最后一位
     * @param string $num 待校验的身份证号
     * @return bool
     */
    private static function isVerifyCodeValid(string $num)
    {
        if (strlen($num) == 18) {
            $factor   = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
            $tokens   = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
            $checkSum = 0;
            for ($i = 0; $i < 17; $i++) {
                $checkSum += intval($num[$i]) * $factor[$i];
            }
            $mod      = $checkSum % 11;
            $token    = $tokens[$mod];
            $lastChar = strtoupper($num[17]);
            if ($lastChar != $token) {
                return false;
            }
        }
        return true;
    }


    /**
     * 防xss过滤
     * @param string $string 请求数据
     * @param bool|False $low 级别
     * @return mixed|string
     */
    public static function cleanXss(&$string, $low = false)
    {
        if (!is_array($string)) {
            $string = trim($string);
            $string = strip_tags($string);
            $string = htmlspecialchars($string);
            if ($low) {
                return $string;
            }
            $string = str_replace(array(
                '"',
                "'",
                "..",
                "../",
                "./",
                '/',
                "//",
                "<",
                ">"
            ), '', $string);
            $no     = '/%0[0-8bcef]/';
            $string = preg_replace($no, '', $string);
            $no     = '/%1[0-9a-f]/';
            $string = preg_replace($no, '', $string);
            $no     = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';
            $string = preg_replace($no, '', $string);
            return $string;
        }
        $keys = array_keys($string);
        foreach ($keys as $key) {
            self::cleanXss($string [$key]);
        }
    }


    /**
     * 检查字符串中是否包含另一字符串
     * @param string $str 被检查的字符串
     * @param string|array $needles 需要包含的字符串
     * @param bool $strict 为true 则检查时区分大小写
     * @return bool
     */
    public static function contains($str, $needles, $strict = true)
    {
        if (!$strict) {
            $str = mb_strtolower($str);
        }
        foreach ((array)$needles as $needle) {
            if (!$strict) {
                $needle = mb_strtolower($needle);
            }
            if ($needle != '' && mb_strpos($str, $needle) !== false) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查字符串是否以某个字符串开头
     * @param string $str 被检查的字符串
     * @param string $needles 需要包含的字符串
     * @param bool $strict 为true 则检查时区分大小写
     * @return bool
     */
    public static function startsWith($str, $needles, $strict = true)
    {
        if (!$strict) {
            $str = mb_strtolower($str);
        }
        foreach ((array)$needles as $needle) {
            if (!$strict) {
                $needle = mb_strtolower($needle);
            }
            if ($needle != '' && mb_strpos($str, $needle) === 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查字符串是否以某个字符串结尾
     * @param string $str 被检查的字符串
     * @param string $needles 需要包含的字符串
     * @param bool $strict 为true 则检查时区分大小写
     * @return bool
     */
    public static function endsWith($str, $needles, $strict = true)
    {
        if (!$strict) {
            $str = mb_strtolower($str);
        }

        foreach ((array)$needles as $needle) {
            if (!$strict) {
                $needle = mb_strtolower($needle);
            }
            if ((string)$needle === mb_substr($str, -mb_strlen($needle))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 驼峰转下划线
     * @param string $value 待处理字符串
     * @param string $delimiter 分隔符
     * @return null|string|string[]
     */
    public static function snake($value, $delimiter = '_')
    {
        if (!ctype_lower($value)) {
            $value = preg_replace('/\s+/u', '', $value);
            $value = mb_strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
        }
        return $value;
    }

    /**
     * 下划线转驼峰 (首字母小写)
     * @param string $value 待处理字符串
     * @return string
     */
    public static function camel($value)
    {
        return lcfirst(self::studly($value));
    }

    /**
     * 下划线转驼峰 (首字母大写)
     * @param string $value 待处理字符串
     * @return mixed
     */
    public static function studly($value)
    {
        $value = ucwords(str_replace(['-', '_'], ' ', $value));
        return str_replace(' ', '', $value);
    }


    /**
     * 解析配置
     * @param string $value 配置值
     * @return array|string
     */
    public static function parseAttr($value = '')
    {
        $array = preg_split('/[,;\r\n]+/', trim($value, ",;\r\n"));
        if (strpos($value, ':')) {
            $value = array();
            foreach ($array as $val) {
                list($k, $v) = explode(':', $val);
                $value[$k] = $v;
            }
        } else {
            $value = $array;
        }
        return $value;
    }
}
